#include"asm_utils.h"
#include"interrupt.h"
#include"stdio.h"
#include"program.h"
#include"thread.h"
#include"sync.h"
#include"memory.h"
#include"stdlib.h"
#include"syscall.h"
#include"tss.h"
#include"shell.h"
#include"arena.h"
#include"file.h"

//屏幕IO处理器
STDIO stdio;
//中断管理器
InterruptManager interruptManager;
//程序管理器
ProgramManager programManager;
//内存管理器
MemoryManager memoryManager;
//系统调用
SystemService systemService;
//Task State Segment
TSS tss;
ByteMemoryManager kernelByteMemoryManager;
FileSystem fileSystem;

int cheese_burger;
SpinLock aLock;
Semaphore semaphore;
void a_mother(void* arg)
{
	// aLock.lock();
	semaphore.P();
	int delay = 0;
	printf("mother: start to make cheese burger, there are %d cheese burger now\n",cheese_burger);
	//make 10 cheese burger
	cheese_burger += 10;

	printf("mother: oh, I have to hang clothes out.\n");
	//hanging clothes out
	delay = 0xfffffff;
	while(delay) delay--;
	//done
	printf("mother: Oh, Jesus! There are %d cheese burgers\n",cheese_burger);
	// aLock.unlock();
	semaphore.V();
}
void a_naughty_boy(void* arg)
{
	// aLock.lock();
	semaphore.P();
	printf("boy   : Look what I found!\n");
	//eat all cheese burgers out secretly
	cheese_burger -= 10;
	//run away as fast as possible
	// aLock.unlock();
	semaphore.V();
}

char book[30];//书的内容
ReaderWriter semaphore_read_write;
void reader1(void* arg)
{
	int i=5;
	while(i--)
	{
		semaphore_read_write.readerEnter();
		printf("reader1 start:%s\n",book);
		int delay=0xffffff;
		while(delay)delay--;//正在读
		printf("reader1 end  :%s\n",book);
		semaphore_read_write.readerExit();
	}
}	
void reader2(void* arg)
{
	int i=5;
	while(i--)
	{
		semaphore_read_write.readerEnter();
		printf("reader2 start:%s\n",book);
		int delay=0xffffff;
		while(delay)delay--;
		printf("reader2 end  :%s\n",book);
		semaphore_read_write.readerExit();
	}
}
void writer1(void* arg)
{
	semaphore_read_write.writerEnter();
	printf("writer1 start:%s\n",book);
	const char* str="wrong1->";
	for(int i=0;str[i]!='\0';i++){
		book[i]=str[i];
	}
	int delay=0xfffffff;
	while(delay)delay--;//正在写
	str="right1";
	for(int i=0;str[i]!='\0';i++){
		book[8+i]=str[i];
	}
	printf("writer1 end  :%s\n",book);
	semaphore_read_write.writerExit();
}
void writer2(void* arg)
{
	semaphore_read_write.writerEnter();
	printf("writer2 start:%s\n",book);
	const char* str="wrong2->";
	for(int i=0;str[i]!='\0';i++){
		book[15+i]=str[i];
	}
	int delay=0xfffffff;
	while(delay)delay--;
	str="right2";
	for(int i=0;str[i]!='\0';i++){
		book[23+i]=str[i];
	}
	printf("writer2 end  :%s\n",book);
	semaphore_read_write.writerExit();
}

//四种解决方案，使用管程监督机制，1.确定线程次序，2.能获取所有资源才行动，否则等待，3.超时放弃所有资源，4.限制资源使用
Semaphore chopsticks[5];
Semaphore tickets;//餐票
//延迟函数，传入时间
void delay(int time=0xffffff)
{
	int delay=time;
	while(delay)delay--;
}
//就餐函数
void dine(void* arg)
{
	char* name=(char*)arg;
	int id=programManager.running->pid;
	// while(true)
	{
		printf("Philosopher%d %s is hungry.\n",id,name);
		delay();
		tickets.P();
		chopsticks[id%5].P();
		printf("Philosopher%d %s picks up left chopstick.\n",id,name);
		delay(0xfffffff);//延迟足够大，哲学家会全部拿左筷子
		chopsticks[(id+1)%5].P();
		printf("Philosopher%d %s picks up right chopstick.\n",id,name);
		delay();
		printf("Philosopher%d %s is eating.\n",id,name);
		delay();
		chopsticks[id%5].V();
		printf("Philosopher%d %s puts down left chopstick.\n",id,name);
		delay();
		chopsticks[(id+1)%5].V();
		printf("Philosopher%d %s puts down right chopstick.\n",id,name);
		tickets.V();
		delay();
		printf("Philosopher%d %s is thinking.\n",id,name);
		delay(0xfffffff);
	}
}

SpinLock memoryLock;
void allocate_thread(void* arg)
{
	memoryLock.lock();
	int* size = (int*)arg;
	int id = programManager.running->pid;
	char* p1 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,*size);
	printf("allocate thread %d: 0x%x \n",id,p1);
	memoryLock.unlock();
}

void LRU_thread(void* arg)
{
	char* p0 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	printf("allocate 0:vaddress %x, %d \n",p0,100);
	printf("*************************************************\n");
	char* p1 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,200);
	printf("allocate 1:vaddress %x, %d \n",p1,200);
	printf("*************************************************\n");
	char* p2 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,15500);
	printf("allocate 2:vaddress %x, %d \n",p2,15500);
	printf("*************************************************\n");
	char* p3 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	printf("allocate 3:vaddress %x, %d \n",p3,100);
	printf("*************************************************\n");
	char* p4 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,200);
	printf("allocate 4:vaddress %x, %d \n",p4,200);
	printf("*************************************************\n");
	char* p5 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,50);
	printf("allocate 5:vaddress %x, %d \n",p5,50);
	printf("*************************************************\n");
	char* p6 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	printf("allocate 6:vaddress %x, %d \n",p6,100);
	printf("*************************************************\n");
}

int syscall_0(int first,int second,int third,int forth,int fifth)
{
	printf("syscall 0: %d %d %d %d %d\n",first,second,third,forth,fifth);
	return first+second+third+forth+fifth;
}
void systemcall_thread(void* arg)
{
	int ret;
	ret = asm_system_call(0);
	printf("return value: %d\n",ret);
	
	ret = asm_system_call(0, 123);
    printf("return value: %d\n", ret);

    ret = asm_system_call(0, 123, 324);
    printf("return value: %d\n", ret);

    ret = asm_system_call(0, 123, 324, 9248);
    printf("return value: %d\n", ret);

    ret = asm_system_call(0, 123, 324, 9248, 7);
    printf("return value: %d\n", ret);

    ret = asm_system_call(0, 123, 324, 9248, 7, 123);
    printf("return value: %d\n", ret);
}

void first_process()
{
	//exit test
	// asm_system_call(0,132,324,12,124);
	// int pid=fork();
	// if(pid==-1)
	// {
	// 	printf("can't fork...\n");
	// 	asm_halt();
	// }else{
	// 	if(pid)
	// 	{
	// 		printf("I'm father,fork return: %d\n",pid);
	// 		asm_halt();
	// 	}else{
	// 		printf("I'm child,fork return: %d ,my pid: %d\n",pid,programManager.running->pid);
	// 	}
	// }
	//wait test
	int pid = fork();
    int retval;

    if (pid)
    {
        pid = fork();
        if (pid)
        {
            // while ((pid = wait(&retval)) != -1)
            {
                printf("wait for a child process, pid: %d, return value: %d\n", pid, retval);
            }

            printf("all child process exit, programs: %d\n", programManager.allPrograms.size());
            exit(0);
            // asm_halt();
        }
        else
        {
            uint32 tmp = 0xffffff;
            while (tmp)
                --tmp;
            printf("exit, pid: %d\n", programManager.running->pid);
            exit(123934);
        }
    }
    else
    {
        uint32 tmp = 0xffffff;
        while (tmp)
            --tmp;
        printf("exit, pid: %d\n", programManager.running->pid);
        exit(-123);
    }
	// asm_halt();
}
void thread_exit(void* arg)
{
	printf("thread exit...\n");
	// exit(0);
}
void shell_process()
{
	int pid = fork();
    
    if(pid == -1) {
        printf("error\n");
        asm_halt();
    } else {
        if(pid) {
            while((pid = wait(nullptr)) != -1) {

            }
            asm_halt();
        } else {
            Shell shell;
            shell.initialize();
            shell.run();
        }
    }
}
void shell_thread(void* arg)
{
	Shell shell;
	shell.initialize();
	shell.run();
	asm_halt();
}
//收养进程，收养父进程退出后的子进程
void init__process()
{
	while(true)
	{
		int pid,retval=0;
		while ((pid = wait(&retval)) != -1)
				printf("init_process:wait for a child process, pid: %d, return value: %d, programs: %d\n", pid, retval,programManager.allPrograms.size());
		// printf("init_process:programs: %d\n", programManager.allPrograms.size());
	}	
	printf("init process exit...\n");
}
int* test=nullptr;
void allocate_process()
{
	printf("allocate process:\n");

	void* p0=malloc(100);
	printf("allocate 0:address %x, %d \n",p0,100);
	free(p0);
	printf("free 0:address %x, %d \n",p0,100);
	void* p1=malloc(100);
	printf("allocate 1:address %x, %d \n",p1,100);
	void* p2=malloc(100);
	printf("allocate 2:address %x, %d \n",p2,100);
	int* p3=(int*)malloc(sizeof(int));
	*p3=1000;
	printf("allocate 3:address %x,value %d\n",p3,*p3);
	free(p3);
	printf("free 3:address %x,value %d\n",p3,*p3);
	char* p4=(char*)malloc(sizeof(char)*31);
	const char* string="10120aff/&%#*fga";	
	strcpy(p4,string);
	printf("allocate 4:address %x,value %s\n",p4,p4);
	free(p4);
	printf("free 4:address %x,value %s\n",p4,p4);

	test=(int*)malloc(sizeof(int));
	*test=123;
	printf("test:%x,value %d\n",test,*test);
	asm_halt();
}
void test_process()
{
	test=(int*)malloc(sizeof(int));
	*test=321;
	printf("test:%x,value %d\n",test,*test);
}
void malloc_thread(void* arg)
{
	printf("allocate thread:\n");

	void* p0=malloc(100);
	printf("allocate 0:address %x, %d \n",p0,100);
	free(p0);
	printf("free 0:address %x, %d \n",p0,100);
	void* p1=malloc(100);
	printf("allocate 1:address %x, %d \n",p1,100);
	void* p2=malloc(100);
	printf("allocate 2:address %x, %d \n",p2,100);
	int* p3=(int*)malloc(sizeof(int));
	*p3=1000;
	printf("allocate 3:address %x,value %d\n",p3,*p3);
	free(p3);
	printf("free 3:address %x,value %d\n",p3,*p3);
	char* p4=(char*)malloc(sizeof(char)*31);
	const char* string="10120aff/&%#*fga";	
	strcpy(p4,string);
	printf("allocate 4:address %x,value %s\n",p4,p4);
	free(p4);
	printf("free 4:address %x,value %s\n",p4,p4);

	test=(int*)malloc(sizeof(int));
	*test=123;
	printf("test:%x,value %d\n",test,*test);
	asm_halt();
}
void file_thread(void*arg)
{
	printf("file:\n\n");

	fileSystem.pwd();
	fileSystem.createFile("lab1",fileType::directory);
	fileSystem.pwd();
	fileSystem.ls();
	fileSystem.echo("\n");

	fileSystem.createFile("lab2",fileType::directory);
	fileSystem.createFile("lab3",fileType::directory);
	fileSystem.pwd();
	fileSystem.ls();
	fileSystem.echo("\n");

	fileSystem.cd("lab1");
	fileSystem.pwd();
	fileSystem.createFile("task1",fileType::directory);
	fileSystem.createFile("readme",fileType::file);
	fileSystem.ls();
	fileSystem.pwd();

	char* buffer=(char*)malloc(sizeof(char)*16);
	fileSystem.read("readme",buffer,16);
	printf("buffer: %s\n",buffer);
	const char* string="10+20aff/fga";
	strcpy(buffer,string);
	fileSystem.write("readme",0,buffer,16);
	strcpy(buffer,"");
	fileSystem.read("readme",buffer,16);
	printf("buffer: %s\n",buffer);

	fileSystem.ls();
	fileSystem.pwd();
	fileSystem.deleteFile("readme");
	fileSystem.ls();
	fileSystem.pwd();
	fileSystem.deleteFile("readme");
	fileSystem.deleteFile("task1");
	fileSystem.ls();
	fileSystem.pwd();

	fileSystem.cd("root");
	fileSystem.ls();
	fileSystem.pwd();
	
	asm_halt();
}
void second_thread(void* arg);
void third_thread(void* arg);
void start_thread(void* arg)
{
	//第一个线程不可以返回
	printf("pid %d, name \"%s\" :Hello ysj!\n",programManager.running->pid,programManager.running->name);
	//清屏
	stdio.clear(0x36);
	//芝士汉堡
	// cheese_burger = 0;
	// semaphore.initialize(1);
	// programManager.executeThread(a_mother,nullptr,"second thread",7);
	// programManager.executeThread(a_naughty_boy,nullptr,"third thread",7);
	//读者写者
	// memset(book,'_',29);
	// book[29]=0;
	// semaphore_read_write.initialize(1);
	// programManager.executeThread(reader1,nullptr,"reader1",7);
	// programManager.executeThread(writer1,nullptr,"writer1",7);
	// programManager.executeThread(reader2,nullptr,"reader1",7);
	// programManager.executeThread(writer2,nullptr,"writer1",7);
	//哲学家就餐问题
	// for(int i=0;i<5;i++) chopsticks[i].initialize(1);
	// tickets.initialize(4);
	// programManager.executeThread(dine,(void*)"Aristotle","philo-1",7);
	// programManager.executeThread(dine,(void*)"Kant","philo-2",7);
	// programManager.executeThread(dine,(void*)"Spinoza","philo-3",7);
	// programManager.executeThread(dine,(void*)"Marx","philo-4",7);
	// programManager.executeThread(dine,(void*)"Russell","philo-5",7);
	// //读取内存大小
	// int memory = *((uint32*)MEMORY_SIZE_ADDRESS);
	// //ax寄存器内容
	// int low = memory&0xffff;
	// //bx寄存器内容
	// int high = (memory>>16)&0xffff;
	// memory = low*1024+high*64*1024;
	// printf("total memory: %d bytes (%d MB)\n",memory,memory/1024/1024);
	// // //分配内存测试
	// char* p1 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	// char* p2 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,10);
	// char* p3 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	// printf("p1=0x%x, p2=0x%x, p3=0x%x\n",p1,p2,p3);
	// memoryManager.releasePages(AddressPoolType::KERNEL,(int)p2,10);
	// p2 = nullptr;
	// printf("p1=0x%x, p2=0x%x, p3=0x%x\n",p1,p2,p3);
	// p2 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,100);
	// printf("p1=0x%x, p2=0x%x, p3=0x%x\n",p1,p2,p3);
	// memoryManager.releasePages(AddressPoolType::KERNEL,(int)p2,100);
	// p2 = (char*)memoryManager.allocatePages(AddressPoolType::KERNEL,10);
	// printf("p1=0x%x, p2=0x%x, p3=0x%x\n",p1,p2,p3);
	// // //线程申请内存
	// int size = 100;
	// programManager.executeThread(allocate_thread,(void*)&size,nullptr,7);
	// programManager.executeThread(allocate_thread,(void*)&size,nullptr,7);
	// programManager.executeThread(allocate_thread,(void*)&size,nullptr,7);
	// int size1 = 10;
	// programManager.executeThread(allocate_thread,(void*)&size1,nullptr,7);
	// // //测试线程，换页机制是否正常运行
	// programManager.executeThread(LRU_thread,nullptr,nullptr,7);
	//测试系统调用功能
	// programManager.executeThread(systemcall_thread,nullptr,nullptr,7);
	//测试进程调度
	printf("start process:\n");
	// programManager.executeProcess((const char*)init__process,7);
	// programManager.executeProcess((const char*)first_process,7);
	// programManager.executeProcess((const char*)first_process,7);	
	// programManager.executeProcess((const char*)first_process,7);
	// programManager.executeThread(thread_exit,nullptr,"second",7);
	//allocate test
	// programManager.executeProcess((const char*)allocate_process,7);
	// programManager.executeProcess((const char*)test_process,7);
	// programManager.executeThread(malloc_thread,nullptr,"malloc thread",7);
	// programManager.executeThread(malloc_thread,nullptr,"malloc thread",7);
	//fileSystem test
	programManager.executeThread(file_thread,nullptr,"file thread",7);
	//shell test
	// programManager.executeProcess((const char*)shell_process,7);
	// programManager.executeThread(shell_thread,nullptr,"shell thread",7);
	// //多级反馈优先队列的调度
	// if(!programManager.running->pid)
	// {
	// 	programManager.executeThread(second_thread,nullptr,"second thread",6);
	// 	programManager.executeThread(second_thread,nullptr,"third thread",3);
	// 	programManager.executeThread(second_thread,nullptr,"forth thread",3);
	// 	programManager.executeThread(second_thread,nullptr,"fifth thread",0);
	// 	programManager.executeThread(third_thread,nullptr,"sixth thread",7);
	// }
	asm_halt();
}
void second_thread(void* arg)
{
	// while(true)
	{
		printf("pid %d, name \"%s\" :Hello ysj!\n",programManager.running->pid,programManager.running->name);
	}
}
void third_thread(void* arg)
{
	int i=0;
	while(i<100000000)
	{
		i++;
	}
	printf("pid %d, name \"%s\" :Hello ysj!\n",programManager.running->pid,programManager.running->name);
}

extern "C" void setup_kernel()
{
	interruptManager.initialize();//中断处理部件
	interruptManager.enableTimeInterrupt();
	interruptManager.setTimeInterrupt((void*)asm_time_interrupt_handler);
	interruptManager.enableKeyboardInterrupt();
	interruptManager.setKeyboardInterrupt((void*)asm_keyboard_interrupt_handler);

	stdio.initialize();//屏幕IO处理部件

	programManager.initialize();//程序管理器

	systemService.initialize();//系统调用
	systemService.setSystemCall(0,(int)syscall_0);
	systemService.setSystemCall(1,(int)syscall_write);
	systemService.setSystemCall(2,(int)syscall_fork);
    systemService.setSystemCall(3, (int)syscall_exit);  
	systemService.setSystemCall(4, (int)syscall_wait);
    systemService.setSystemCall(5, (int)syscall_move_cursor);
	systemService.setSystemCall(6, (int)syscall_malloc);
    systemService.setSystemCall(7, (int)syscall_free);

	memoryManager.initialize();//内存管理器
	kernelByteMemoryManager.initialize();

	fileSystem.initialize();
	int pid=programManager.executeThread(start_thread,nullptr,"first thread",7);
	if(pid==-1)
	{
		printf("can not execute thread!");
		asm_halt();
	}
	ListItem* item=programManager.readyPrograms.front();
	PCB* firstThread=ListItemToPCB(item,tagInGeneralList);
	firstThread->status=RUNNING;
	programManager.readyPrograms.pop_front();
	programManager.running=firstThread;
	asm_switch_thread(0,firstThread);
	// programManager.schedule_first_in_first_out();
	// programManager.schedule_multilevel_feedback_queue();
	asm_halt();
}
